package cc.rengu.igas.channel.wechat.core.service.trans;


import cc.rengu.igas.channel.wechat.common.constant.WechatConfigConstant;
import cc.rengu.igas.channel.wechat.common.constant.WechatParamConstant;
import cc.rengu.igas.channel.wechat.common.constant.WechatServiceConstant;
import cc.rengu.igas.channel.wechat.common.constant.WechatTreeNodeConstant;
import cc.rengu.igas.channel.wechat.common.enums.RespCodeEnum;
import cc.rengu.igas.channel.wechat.core.model.ChnlBizResponse;
import cc.rengu.igas.channel.wechat.core.realize.WechatSignService;
import cc.rengu.igas.channel.wechat.core.realize.impl.WechatSignServiceImpl;
import cc.rengu.igas.channel.wechat.core.service.base.PayService;
import cc.rengu.igas.channel.wechat.facade.bean.ContactInfo;
import cc.rengu.igas.channel.wechat.facade.bean.IdentificationInfo;
import cc.rengu.igas.channel.wechat.facade.request.WechatRealNameApplymentRequest;
import cc.rengu.igas.channel.wechat.facade.response.WechatRealNameApplymentResponse;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.common.dao.CertInfoMapper;
import cc.rengu.oltp.service.common.dao.KeyInfoMapper;
import cc.rengu.oltp.service.common.dao.SecPlanInfoMapper;
import cc.rengu.oltp.service.common.dao.impl.CertInfoMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.KeyInfoMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.SecPlanInfoMapperImpl;
import cc.rengu.oltp.service.common.entity.*;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.service.realize.SysParamService;
import cc.rengu.oltp.service.realize.impl.SysParamServiceImpl;
import cc.rengu.oltp.utility.util.*;
import cc.rengu.utility.cache.UnifiedCache;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.codec.binary.Base64;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.interfaces.RSAPublicKey;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;


/**
 * 微信实名验证提交申请
 */
public class AuthApplymentService extends PayService {

    @Override
    protected Object beforeBizProcess() throws Exception {
        WechatRealNameApplymentRequest realNameApplymentRequest = new WechatRealNameApplymentRequest();
        ConvertUtil.convertOutput(realNameApplymentRequest);
        return realNameApplymentRequest;
    }

    @Override
    protected ChnlBizResponse callBizService(Object request) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        int iReturnCode = 0;

        WechatRealNameApplymentResponse realNameApplymentResponse = new WechatRealNameApplymentResponse();
        WechatRealNameApplymentRequest realNameApplymentRequest = (WechatRealNameApplymentRequest) request;

        DstChannelCfg dstChannelCfg = (DstChannelCfg) xmlTreeUtil.getXmlTreeObjectValue(WechatTreeNodeConstant.DST_CHANNEL_CFG, DstChannelCfg.class);
        DstChannelInfo dstChannelInfo = (DstChannelInfo) xmlTreeUtil.getXmlTreeObjectValue(WechatTreeNodeConstant.DST_CHANNEL_INFO, DstChannelInfo.class);
        SysParamService sysParamService = new SysParamServiceImpl();
        SysParam sysParam = sysParamService.getSysParamInfo(realNameApplymentRequest.getHeader().getInstId(),
                WechatConfigConstant.WECHAT_CONF_NODE, WechatConfigConstant.WECHAT_APPLYMENT_URL);
        if (sysParam == null) {
            rglog.error("获取微信实名验证申请url失败，请配置");
            throw new BizException(RespCodeEnum.CONFIG_PARAM_NOT_EXIST.getRespCode(), RespCodeEnum.CONFIG_PARAM_NOT_EXIST.getRespDesc());
        }
        xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.TXN_NUM, realNameApplymentRequest.getHeader().getTxnNum());
        encSensitiveInfo(realNameApplymentRequest, dstChannelCfg);
        ConvertUtil.convertInput(realNameApplymentRequest);
        xmlTreeUtil.setXmlTreeStringValue("wechatChannelId", dstChannelCfg.getChannelNo());
        xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.DEFINE_HTTP_URL_NODE, sysParam.getParamValue());
        xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.DEFINE_HTTP_METHOD_NODE, "POST");
        //走  V3客户端  WechatAuthClientService
        iReturnCode = schedule.callService(WechatServiceConstant.WECHAT_V3_CLIENT_SERVICE, 0);

        if (xmlTreeUtil.isExist(WechatTreeNodeConstant.DEFINE_HTTP_URL_NODE)) {
            xmlTreeUtil.deleteXmlTreeNode(WechatTreeNodeConstant.DEFINE_HTTP_URL_NODE);
        }

        if (0 != iReturnCode && 98 != iReturnCode) {
            rglog.error("调用微信服<{}>失败，iReturnCode:<{}>", dstChannelInfo.getDstServiceName(), iReturnCode);
            throw new BizException(RespCodeEnum.CALL_BIZ_SERVICE_ERROR.getRespCode(), RespCodeEnum.CALL_BIZ_SERVICE_ERROR.getRespDesc());
        } else if (98 == iReturnCode) {
            rglog.error("调用微信服<{}>超时，iReturnCode:<{}>", dstChannelInfo.getDstServiceName(), iReturnCode);
            throw new BizException(RespCodeEnum.TIME_OUT_ERROR.getRespCode(), RespCodeEnum.TIME_OUT_ERROR.getRespDesc());
        }
        //ConvertUtil.convertOutput(realNameApplymentResponse);
        rglog.info("applymentId = <{}>", xmlTreeUtil.getXmlTreeStringValue("applymentId"));
        realNameApplymentResponse.setApplymentId(String.valueOf(xmlTreeUtil.getXmlTreeStringValue("applymentId")));
        String returnCode = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.WX_RETURN_CODE);
        String returnMsg = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.WX_RETURN_MSG);
        if (!WechatParamConstant.STRING_SUCCESS.equals(returnCode)) {
            realNameApplymentResponse.setRespCode(returnCode);
            realNameApplymentResponse.setRespDesc(returnMsg);
            rglog.error("调用第三方服务失败,respCode=[{}],respDesc=[{}]", realNameApplymentResponse.getRespCode(), realNameApplymentResponse.getRespDesc());
        } else {
            realNameApplymentResponse.setRespCode(RespCodeEnum.TRANS_SUCCESS.getRespCode());
            realNameApplymentResponse.setRespDesc(RespCodeEnum.TRANS_SUCCESS.getRespDesc());
        }
        ChnlBizResponse chnlBizResponse = new ChnlBizResponse();
        chnlBizResponse.setResult(realNameApplymentResponse);
        return chnlBizResponse;
    }

    @Override
    protected void afterBizProcess(ChnlBizResponse response) throws Exception {
        WechatRealNameApplymentResponse realNameApplymentResponse = (WechatRealNameApplymentResponse) response.getResult();
        ConvertUtil.convertInput(realNameApplymentResponse);
    }

    /**
     * 加密敏感信息
     *
     * @param realNameApplymentRequest
     * @param dstChannelCfg
     * @throws Exception
     */
    private void encSensitiveInfo(WechatRealNameApplymentRequest realNameApplymentRequest, DstChannelCfg dstChannelCfg) throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String instId = realNameApplymentRequest.getHeader().getInstId();
        if (realNameApplymentRequest.getIdentificationInfo() == null ||
                realNameApplymentRequest.getContactInfo() == null ||
                realNameApplymentRequest.getSubjectInfo() == null) {
            rglog.error("必须上送身份验证信息");
            throw new BizException(RespCodeEnum.MSG_VALID_CHECK_ERROR.getRespCode(), RespCodeEnum.MSG_VALID_CHECK_ERROR.getRespDesc());
        }
        CertInfo certInfo = null;
        /* 根据通道安全计划获取安全计划 */
        SecPlanInfoMapper secPlanInfoMapper = new SecPlanInfoMapperImpl();
        List<SecPlanInfo> secPlanInfoList = secPlanInfoMapper.selectSecPlanInfoBySecPlanId(instId, dstChannelCfg.getSecPlanId());
        if (null != secPlanInfoList && !secPlanInfoList.isEmpty()) {
            Optional<SecPlanInfo> secPlanInfoOpt = secPlanInfoList.stream().filter(item -> item.getSecPlanType().equals(WechatParamConstant.ENCRYPT_CERT_TYPE)).findFirst();
            if (secPlanInfoOpt.isPresent()) {
                CertInfoMapper certInfoMapper = new CertInfoMapperImpl();
                certInfo = certInfoMapper.selectCertInfoByPrimaryKey(instId + secPlanInfoOpt.get().getSecPlanId() + secPlanInfoOpt.get().getSecPlanType());
            }
        }
        if (certInfo == null) {
            rglog.error("微信服务商[{}]未配置加密公钥", realNameApplymentRequest.getChannelType());
            throw new BizException(RespCodeEnum.PUB_CERT_NOT_EXIST.getRespCode(), RespCodeEnum.PUB_CERT_NOT_EXIST.getRespDesc());
        }
        xmlTreeUtil.setXmlTreeStringValue("_MESSAGEHEAD/http/Wechatpay-Serial", certInfo.getCertDn());
        WechatSignService wechatSignService = new WechatSignServiceImpl();
        Certificate certificate = (Certificate) UnifiedCache.get("WECHATV3PLAT", certInfo.getCertDn());
        if (null == certificate) {
            rglog.error("证书未在本地缓存中，请刷新本地缓存后处理，但此功能未开发，建议重新上传图片以重新加载缓存");
            certificate = getV3WechatPlatCert(instId, dstChannelCfg);
        }
        PublicKey publicKey = certificate.getPublicKey();
        /*身份信息加密*/
        IdentificationInfo identificationInfo = realNameApplymentRequest.getIdentificationInfo();
        if (identificationInfo != null) {
            if (identificationInfo.getCertNo() != null) {
                identificationInfo.setCertNo(Base64.encodeBase64String(RSAUtil.rsaEcbEncrypt((RSAPublicKey) publicKey, identificationInfo.getCertNo().getBytes(), "OAEPWithSHA-1AndMGF1Padding")));
                //identificationInfo.setCertNo(wechatSignService.rsaEncSensitiveInfo(identificationInfo.getCertNo(),certInfo,"OAEPWithSHA-1AndMGF1Padding"));
            }
            if (identificationInfo.getName() != null) {
                identificationInfo.setName(Base64.encodeBase64String(RSAUtil.rsaEcbEncrypt((RSAPublicKey) publicKey, identificationInfo.getName().getBytes(), "OAEPWithSHA-1AndMGF1Padding")));

                //identificationInfo.setName(wechatSignService.rsaEncSensitiveInfo(identificationInfo.getName(),certInfo,"OAEPWithSHA-1AndMGF1Padding"));
            }
            ConvertUtil.convertInput(identificationInfo);
        }
        /*联系人信息*/
        ContactInfo contactInfo = realNameApplymentRequest.getContactInfo();
        if (contactInfo != null) {
            if (contactInfo.getContactName() != null) {
                contactInfo.setContactName(Base64.encodeBase64String(RSAUtil.rsaEcbEncrypt((RSAPublicKey) publicKey, contactInfo.getContactName().getBytes(), "OAEPWithSHA-1AndMGF1Padding")));

                //contactInfo.setContactName(wechatSignService.rsaEncSensitiveInfo(contactInfo.getContactName(),certInfo,"OAEPWithSHA-1AndMGF1Padding"));
            }
            if (contactInfo.getContactPhone() != null) {
                contactInfo.setContactPhone(Base64.encodeBase64String(RSAUtil.rsaEcbEncrypt((RSAPublicKey) publicKey, contactInfo.getContactPhone().getBytes(), "OAEPWithSHA-1AndMGF1Padding")));

                //contactInfo.setContactPhone(wechatSignService.rsaEncSensitiveInfo(contactInfo.getContactPhone(),certInfo,"OAEPWithSHA-1AndMGF1Padding"));
            }
            if (contactInfo.getCertId() != null) {
                contactInfo.setCertId(Base64.encodeBase64String(RSAUtil.rsaEcbEncrypt((RSAPublicKey) publicKey, contactInfo.getCertId().getBytes(), "OAEPWithSHA-1AndMGF1Padding")));

                // contactInfo.setCertId(wechatSignService.rsaEncSensitiveInfo(contactInfo.getCertId(),certInfo,"OAEPWithSHA-1AndMGF1Padding"));
            }
            ConvertUtil.convertInput(contactInfo);
        }
    }

    private Certificate getV3WechatPlatCert(String instId, DstChannelCfg dstChannelCfg) throws Exception {
        SysParamService sysParamService = new SysParamServiceImpl();
        SysParam sysParam = sysParamService.getSysParamInfo(instId, "wechatV3", "proxyHost");
        if (null == sysParam) {
            rglog.error("未配置微信V3接口代理服务器,wechatV3,proxyHost");
            throw new BizException(RespCodeEnum.CONFIG_PARAM_NOT_EXIST.getRespCode(), RespCodeEnum.CONFIG_PARAM_NOT_EXIST.getRespDesc());
        }
        String proxyHost = sysParam.getParamValue();
        sysParam = sysParamService.getSysParamInfo(instId, "wechatV3", "proxyPort");
        if (null == sysParam) {
            rglog.error("未配置微信V3接口代理服务器,wechatV3,proxyPort");
            throw new BizException(RespCodeEnum.CONFIG_PARAM_NOT_EXIST.getRespCode(), RespCodeEnum.CONFIG_PARAM_NOT_EXIST.getRespDesc());
        }
        int proxyPort = Integer.valueOf(sysParam.getParamValue()).intValue();
        //获取商户证书
        CertInfo certInfo = null;
        /* 根据通道安全计划获取安全计划 */
        SecPlanInfoMapper secPlanInfoMapper = new SecPlanInfoMapperImpl();
        List<SecPlanInfo> secPlanInfoList = secPlanInfoMapper.selectSecPlanInfoBySecPlanId(instId, dstChannelCfg.getSecPlanId());
        if (null != secPlanInfoList && !secPlanInfoList.isEmpty()) {
            Optional<SecPlanInfo> secPlanInfoOpt = secPlanInfoList.stream().filter(item -> item.getSecPlanType().equals(WechatParamConstant.V3_SIGN_CERT_TYPE)).findFirst();
            if (secPlanInfoOpt.isPresent()) {
                CertInfoMapper certInfoMapper = new CertInfoMapperImpl();
                certInfo = certInfoMapper.selectCertInfoByPrimaryKey(instId + secPlanInfoOpt.get().getSecPlanId() + secPlanInfoOpt.get().getSecPlanType());
            }
        }
        if (certInfo == null) {
            rglog.error("未配置商户密钥");
            throw new BizException(RespCodeEnum.PUB_CERT_NOT_EXIST.getRespCode(), RespCodeEnum.PUB_CERT_NOT_EXIST.getRespDesc());
        }
        // 换行符
        String LINE_END = "\r\n";
        String PREFIX = "--";
        // 定义数据分隔线
        String BOUNDARY = UUID.randomUUID().toString().replaceAll("-", "");
        //时间戳
        String timestamp = Long.toString(System.currentTimeMillis() / 1000);
        //随机数
        String nonce_str = RandomUtil.randomString(32);

        //拼签名串
        StringBuffer sb = new StringBuffer();
        sb.append("GET").append("\n");
        sb.append("/v3/certificates").append("\n");
        sb.append(timestamp).append("\n");
        sb.append(nonce_str).append("\n");
        sb.append("\n");
        rglog.info("签名原串:<{}>", sb.toString());

        WechatSignService wechatSignService = new WechatSignServiceImpl();
        String signData = wechatSignService.generateSignString(sb.toString(), certInfo);

        //拼装http头的Authorization内容
        String authorization = "WECHATPAY2-SHA256-RSA2048 mchid=\"" + dstChannelCfg.getProviderMchntNo() + "\",nonce_str=\"" + nonce_str + "\",signature=\"" + signData + "\",timestamp=\"" + timestamp + "\",serial_no=\"" + certInfo.getCertDn() + "\"";
        rglog.debug("authorization值:" + authorization);
        String certDownloadUrl = "https://api.mch.weixin.qq.com/v3/certificates";
        //接口URL
        URL url = new URL(certDownloadUrl);
        InetSocketAddress addr = new InetSocketAddress(proxyHost, proxyPort);
        Proxy proxy = new Proxy(Proxy.Type.HTTP, addr);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection(proxy);
        // 设置为POST
        conn.setRequestMethod("GET");
        // 发送POST请求必须设置如下两行
        conn.setUseCaches(false);
        // 设置请求头参数
        conn.setRequestProperty("Accept", "application/json");
        conn.setRequestProperty("Authorization", authorization);
        conn.setRequestProperty("User-Agent", "https://zh.wilipedia.org/wiki/User_agent");

        OutputStream outputStream = null;
        InputStream is = null;
        try {
            outputStream = conn.getOutputStream();
        } catch (IOException e) {
            rglog.error(e.getMessage());
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (Exception e) {
                    rglog.error(e.getMessage());
                }
            }
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (Exception e) {
                    rglog.error(e.getMessage());
                }
            }
        }
        //接收返回
        //打印返回头信息
        rglog.debug("接口返回头信息:");
        Map<String, List<String>> responseHeader = conn.getHeaderFields();
        for (Map.Entry<String, List<String>> entry : responseHeader.entrySet()) {
            rglog.debug(entry.getKey() + ":" + entry.getValue());
        }
        String data = "";
        //打印返回内容
        int responseCode = conn.getResponseCode();
        String rescontent = "";
        InputStream inputStream = null;
        try {
            if ((String.valueOf(responseCode)).startsWith("2")) {
                //成功
                inputStream = conn.getInputStream();
                rescontent = new String(InputStreamTOByte(inputStream));
                rglog.debug("证书下载成功:" + rescontent);
                JSONObject json = JSONObject.parseObject(rescontent);
                JSONArray dataArray = json.getJSONArray("data");
                //解密微信平台证书，并更新
                /* 根据通道安全计划获取安全计划 */
                KeyInfoMapper keyInfoMapper = new KeyInfoMapperImpl();
                KeyInfo keyInfo = new KeyInfo();
                if (null != secPlanInfoList && !secPlanInfoList.isEmpty()) {
                    Optional<SecPlanInfo> secPlanInfoOpt = secPlanInfoList.stream().filter(item -> item.getSecPlanType().equals(WechatParamConstant.V3_AES_KEY_TYPE)).findFirst();
                    if (secPlanInfoOpt.isPresent()) {
                        keyInfo = keyInfoMapper.selectKeyInfoByPrimaryKey(instId + secPlanInfoOpt.get().getSecPlanId() + secPlanInfoOpt.get().getSecPlanType());
                    }
                }
                JSONObject allCertInfo = (JSONObject) dataArray.get(0);
                String serialNo = allCertInfo.getString("serial_no");
                rglog.info("serialNo = <{}>", serialNo);
                JSONObject encryptCert = allCertInfo.getJSONObject("encrypt_certificate");
                rglog.info("encryptCert = <{}>", encryptCert);
                String nonce = encryptCert.getString("nonce");
                rglog.info("nonce = <{}>", nonce);
                String associatedData = encryptCert.getString("associated_data");
                rglog.info("associatedData = <{}>", associatedData);
                String cipherText = encryptCert.getString("ciphertext");
                rglog.info("cipherText = <{}>", cipherText);
                rglog.debug("keyInfo = <{}>", JSON.toJSONString(keyInfo));
                String apiV3AesSecret = keyInfo.getKeyValue();
                String wechatPlatCertValue = AESUtil.gcmDecryptToString(apiV3AesSecret.getBytes(), associatedData.getBytes(), nonce.getBytes(), cipherText);
                //更新到微信平台证书中
                if (!StringUtil.isEmptyOrNull(wechatPlatCertValue)) {
                    //需处理wechatPlatCertValue值得证书头和尾
                    // wechatPlatCertValue = wechatPlatCertValue.substring(wechatPlatCertValue.indexOf("\n"),wechatPlatCertValue.lastIndexOf("\n")).replaceAll("\n","");
                    CertificateFactory cf = CertificateFactory.getInstance("X509");
                    ByteArrayInputStream inputStream1 = new ByteArrayInputStream(wechatPlatCertValue.getBytes(StandardCharsets.UTF_8));
                    Certificate certificate = null;
                    try {
                        certificate = cf.generateCertificate(inputStream1);
                    } catch (CertificateException ce) {
                        ce.printStackTrace();
                    }
                    UnifiedCache.set("WECHATV3PLAT", serialNo, certificate);
                    return certificate;
                }
            } else {
                //失败
                inputStream = conn.getErrorStream();
                rescontent = new String(InputStreamTOByte(inputStream));
                rglog.debug("证书下载失败:" + rescontent);
                return null;
            }
        } catch (Exception e) {
            rglog.error(e.getMessage());
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (Exception e) {
                    rglog.error(e.getMessage());
                }
            }
        }
        return null;
    }

    public static byte[] InputStreamTOByte(InputStream in) throws IOException {

        int BUFFER_SIZE = 4096;
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        byte[] data = new byte[BUFFER_SIZE];
        int count = -1;

        while ((count = in.read(data, 0, BUFFER_SIZE)) != -1) {
            outStream.write(data, 0, count);
        }

        data = null;
        byte[] outByte = outStream.toByteArray();
        try {
            if (outStream != null) {
                outStream.close();
            }
        } catch (Exception e) {

        }
        return outByte;
    }
}
